﻿#region License statement
// NJamb, a specification and delta-specification DSL
// Copyright (c) 2010-2011, Mark Knell
// Published under the MIT License; all other rights reserved
#endregion

#region using...
using System;
using NJamb.Parser.Subjects;
using NJamb.Specifications;
using NJamb.Specifications.Delta;
using NJamb.Specifications.Simple;
using NJamb.Specifications.Simple.Singular;
using Stile.Patterns.SelfDescribingPredicates;
using Stile.Patterns.SelfDescribingPredicates.Evaluators;
#endregion

namespace NJamb.Parser.SpecificationBuilders.Delta
{
    public interface IDeltaSpecificationBuilder<out TDelta> : ICanThrow<TDelta>
        where TDelta : class, IDeltaSpecification {}

    public interface IDeltaSpecificationBuilder : IDeltaSpecificationBuilder<IDeltaSpecification> {}

    public abstract class DeltaSpecificationBuilder<TSubject, TCollaboratingSubject, TEvaluation, TSpecification, TDelta> :
        ICanThrow<TDelta>
        where TCollaboratingSubject : class, ICollaboratingSubject
        where TEvaluation : class, IEvaluation
        where TSpecification : class, ICollaboratingSpecification<TCollaboratingSubject, TEvaluation>
        where TDelta : class, IDeltaSpecification
    {
        protected DeltaSpecificationBuilder(TSpecification before)
        {
            Before = before;
        }

        public TDelta Throws<TException>() where TException : Exception
        {
            var evaluator = new ExceptionEvaluator<TSubject, TException>();

            Func<Exception, TSubject, IEvaluation<TSubject>> exceptionFilter =
                SpecificationBuilderExtensions.MakeExceptionFilter<TSubject, TException>(
                    Before.CollaboratingSubject.LazyDescription);

            return Specify(evaluator, exceptionFilter);
        }

        public abstract TDelta Specify(ISelfDescribingPredicate<TSubject> selfDescribingPredicate,
            Func<Exception, TSubject, IEvaluation<TSubject>> exceptionFilter = null);

        protected TSpecification Before { get; set; }
    }

    public abstract class DeltaSpecificationBuilder<TSubject> :
        DeltaSpecificationBuilder
            <TSubject, ICollaboratingSubject<TSubject>, IEvaluation<TSubject>, ICollaboratingSimpleSpecification<TSubject>,
                IDeltaSpecification>
    {
        protected DeltaSpecificationBuilder(ICollaboratingSimpleSpecification<TSubject> before)
            : base(before) {}

        public override IDeltaSpecification Specify(ISelfDescribingPredicate<TSubject> selfDescribingPredicate,
            Func<Exception, TSubject, IEvaluation<TSubject>> exceptionFilter = null)
        {
            IEvaluable<IEvaluation<TSubject>> after = new SingularSpecification<TSubject>(Before.CollaboratingSubject,
                selfDescribingPredicate,
                exceptionFilter : exceptionFilter);
            return new DeltaSpecification<TSubject>(Before, after);
        }
    }
}
